package eval

import (
	"showen/lex"
	"showen/obj"
	"showen/parse"
	"testing"
)

func TestCaseIntegration(t *testing.T) {
	tests := []struct {
		name      string
		src       string
		expected  string
		expectErr bool
	}{{
		name:      "simple case expression with integer key",
		src:       "(case 2 ((1) \"one\") ((2) \"two\") ((3) \"three\"))",
		expected:  "\"two\"",
		expectErr: false,
	},
		{
			name:      "case expression with multiple datums in clause",
			src:       "(case 5 ((1 2 3) \"small\") ((4 5 6) \"medium\") ((7 8 9) \"large\"))",
			expected:  "\"medium\"",
			expectErr: false,
		},
		{
			name:      "case expression with else clause",
			src:       "(case 10 ((1 2 3) \"small\") (else \"other\"))",
			expected:  "\"other\"",
			expectErr: false,
		},
		{
			name:      "case expression with no matches and no else",
			src:       "(case 10 ((1 2 3) \"small\") ((4 5 6) \"medium\"))",
			expected:  "",
			expectErr: false,
		},
		{
			name:      "case expression with boolean key",
			src:       "(case #t ((#f) \"false\") ((#t) \"true\"))",
			expected:  "\"true\"",
			expectErr: false,
		},
		{
			name:      "case expression with string key",
			src:       "(case \"hello\" ((\"world\") \"world response\") ((\"hello\") \"hello response\"))",
			expected:  "\"hello response\"",
			expectErr: false,
		},
		{
			name:      "case expression with variable as key",
			src:       "(define x 3) (case x ((1) \"one\") ((2) \"two\") ((3) \"three\"))",
			expected:  "\"three\"",
			expectErr: false,
		},
		{
			name:      "case expression with expression as key",
			src:       "(case (+ 2 3) ((1) \"one\") ((5) \"five\") ((10) \"ten\"))",
			expected:  "\"five\"",
			expectErr: false,
		},
		{
			name:      "case expression with multiple expressions in body",
			src:       "(case 2 ((2) 1 2 3 42))",
			expected:  "42",
			expectErr: false,
		},
		{
			name:      "nested case expressions",
			src:       "(case 2 ((1) \"one\") ((2) (case 3 ((3) \"three\"))))",
			expected:  "\"three\"",
			expectErr: false,
		},
		{
			name:      "case with quote special form",
			src:       `(case 'apple (('banana) "banana") (('apple) "apple"))`,
			expected:  "\"apple\"",
			expectErr: false,
		},
		{
			name:      "case with complex expressions in datums",
			src:       "(case 5 ((4) \"four\") ((5) \"five\"))",
			expected:  "\"five\"",
			expectErr: false,
		},
		{
			name:      "case with else clause and match",
			src:       "(case 1 ((1) \"one\") (else \"first\"))",
			expected:  "\"one\"",
			expectErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Create a new environment for each test
			env := obj.NewEnvironment()
			RegisterBuiltinProcedures(env)

			// Lex
			lexer := lex.NewLexer(tt.src)
			tokens := lexer.ReadTokens()

			// Parse
			parser := parse.NewParser(tokens)
			program, err := parser.ParseProgram()
			if tt.expectErr {
				if err == nil {
					t.Errorf("expected error, got nil")
				}
				return
			}
			if err != nil {
				t.Errorf("parse error: %v", err)
				return
			}

			// Eval
			var result obj.Object
			for _, expr := range program {
				result, err = EvalExpression(expr, env)
				if err != nil {
					t.Errorf("eval error: %v", err)
					return
				}
			}

			if result == nil {
				t.Errorf("expected result, got nil")
				return
			}
			// Check if the result matches the expected value
			if result.Inspect() != tt.expected {
				t.Errorf("expected %v, got %v", tt.expected, result.Inspect())
				return
			}
		})
	}
}
